import mosal_a
__author__="furoscame"
__date__ ="29.12.11 23:28"
debug = False
"""
This class handles the communication to and from leo via serial interface
It tries to find leonardo by scanning the first 9 com ports and requests MOSAL-A protocol
if it gets the PAK byte and the MOSAL open message it assumes that this is leo
"""
import pickle
from mosal_a import MosalA
import serial
import time
import threading
import re

class LeoSerial:
    """
    This class hanles the low level communication to leo
    """
    def __init__(self):
        """
        Do the init
        """
        self.comPort = serial.Serial()
        self.open = False
        self.old_message = "\r"
    pass

    def _scan(self):
        """
        Scan for available ports. return a list of tuples (num, name)
        """
        available = []
        for i in range(10):
            try:
                s = serial.Serial(i) # if not existing an exception occured
                available.append((i, s.portstr))
                s.close()   # explicit close 'cause of delayed GC in java
                if debug == True:
                    print ("Com:", i+1)
            except serial.SerialException:
                if debug == True:
                    print ("Not to open", i)
                # needed to cope with comp witch are not to open
                pass
        return available
    pass

    def open_leo_com(self):
        """
            It goes through all connected ports and try to talk to leonardo
        """

        mosal_a = MosalA()
        if self.quick_open() == True:
            return

        available = self._scan()
        print ("Finished scan!")
        for p in available:
            self.comPort = serial.Serial( p[0] ) # set the port as number
            self.comPort.baudrate = 1200
            self.comPort.timeout = 0.5

            if debug == True:
                print ("Request Mosal")
            res = self._request_mosal()

            if res == "no":
                print ("Seems to be no Leonardo!")
                continue
            elif res == "some":
                print ("Try it again with 9600 baud")

            elif res == "leo":
                print ("Caught leonardo at 1200 baud")
                print ("Try to set 9600 baud")
                self._set_9600_by_keys()
            pass

            # try 9600 baud
            self.comPort.close()
            self.comPort.baudrate = 9600
            self.comPort.open()
            time.sleep(0.4)

            self.comPort.flushInput()
            self.comPort.flushOutput()

            # if previosly tried to set to 9600 than leo is in setup mode therefore set him to normal mode by key 13
            if res == "leo":
                keystr = mosal_a.get_key_cmd(13)
                self.write(keystr + "\r")
                time.sleep(0.4)

            res = self._request_mosal()

            if res == "no":
                print ("Dont know what went wrong!")
                continue
            elif res == "some":
                print ("Possibly an odd baud rate")
                print ("Please set it to 9600 baud")
                continue

            # last protocol check
            if self._validate_leonardo() == False:
                continue
            else:
                self.save_connection_data()
                print ("Leonardo is online with 9600 baud!")
                break
        pass
    pass

    def quick_open(self):
        try:
            cd = pickle.load(open("leonardo_serial.inf", "rb"))
        except:
            if debug == True:
                print ("leonardo_serial.inf not found!")
            return False

        try:
            self.comPort.port = cd.port
            self.comPort.baudrate = cd.baud
            self.comPort.timeout = 0.5
            self.comPort.open()
        except:
            if debug == True:
                print ("Port information in leonardo_serial.inf not sufficient!")
            return False
        
        res = self._request_mosal()

        if res == "leo":
            if self._validate_leonardo() == True:
                print ("I did a quick connect :-)")
                return True
        pass
        self.comPort.close()
        return False
    pass

    def save_connection_data(self):
        cd = ConnectionData(self.comPort.port, self.comPort.baudrate)
        pickle.dump(cd,open ("leonardo_serial.inf", "wb"))
        pass
    pass

    def _validate_leonardo(self):
        # validate if realy a leonardo is on wire
        mosal_a = MosalA()
        mosal_str = mosal_a.get_open_cmd()
        if debug == True:
            print (mosal_str)
        self.write(mosal_str + "\r")
        time.sleep(0.5)

        resp = self.comPort.readline()
        mes = mosal_a.get_message_from_mosal_string(resp)

        if mes.message == "Open" :
            self.open = True
            time.sleep(0.1)
            mosal_str = mosal_a.get_beep_cmd()
            self.write(mosal_str  + "\r")
            return True
        else:
            print ("Looks like leonardo but quacks not like leonardo")
            return False

    pass

    def _set_9600_by_keys(self):
        # set 9600 baud rate by key commands
        mosal_a = MosalA()
        keystr = mosal_a.get_key_cmd(17) + "\r"
        self.write(keystr)
        time.sleep(0.5)

        print (self.comPort.readline())
        keystr = mosal_a.get_key_cmd(8) + "\r"
        self.write(keystr)

        time.sleep(0.5)
        print (self.comPort.readline())
        keystr = mosal_a.get_key_cmd(1) + "\r"
        self.write(keystr)
        pass
    pass

    def _request_mosal(self):
        mosal_a = MosalA()
        ret = "no"
        #request Mosal A
        resp = " "
        i = 0
        while resp[0] != mosal_a.PAK and i < 3:
            self.comPort.write( mosal_a.RMA + "\r")
            #print (mosal_a.RMA)
            time.sleep(0.4)
            resp = self.comPort.readline()
            resp = resp.rstrip('\r\n')
            if len(resp) == 0:
                if debug == True:
                    print ("No response!")
                ret = "no"
                resp = " "
            elif resp[0] == mosal_a.PAK:
                if debug == True:
                    print ("Found Leonardo!")
                ret = "leo"
            else:
                if debug == True:
                    print ("Found something!", resp)
                ret = "some"
                pass
            i += 1
            pass

        return ret
    pass

    def write(self, message):
        """
        The method to write
        """
        self.old_message = message  # needed for rewrite in case of error ^BEL
        self.comPort.write(message)
    pass

    def shortcut(self):
        """
        Go reading the serial port from leo
        """

        self.alive = True
        self.thread_read = threading.Thread(target=self.reader2)
        self.thread_read.setDaemon(True)
        self.thread_read.setName('Leo->serial')
        self.thread_read.start()
    pass

    def reader2 (self):
        mosal_a = MosalA()
        while self.alive:
            
            data = self.comPort.readline()            
            data = data.strip('\n\r')
            
            if len(data) >0:
                print (data)
                if ord(data[0]) == 170: # PAK byte ?
                    print (ord(data[0]))
                elif ord(data[0]) == mosal_a.BEL:
                    self.write(self.old_message)
                    print ("Low level resend:" , self.old_message, data)
                    continue
                
                if re.match('[0-9ABCDEF]+',data) == False:
                    print ("Garbage:" , data)
                    continue
                else:
                    mo = re.match('[0-9ABCDEF]+',data)
                    data = mo.group(0)
                if debug == True:
                    print ("serial", data)   #, self.comPort
                self.receiver(data)
        pass
    pass
    def reader(self):
        """loop forever and copy serial->socket"""
        while self.alive:
            try:
                data = self.comPort.read(1)              # read one, blocking
                n = self.comPort.inWaiting()             # look if there is more
                if n:
                    data = data + self.comPort.read(n)   # and get as much as possible
                if data:
                    self.receiver(data)
                if shutdown:
                    print ("Shutdown read")
                    self.alive = False
                print ("data", data)
            except :
                pass
            #   sys.stderr.write('ERROR: %s\n' % msg)
            # probably got disconnected
            #
            #         pass
        self.alive = False
        pass
    pass

    def set_receiver_proc(self, receiver):
        self.receiver = receiver
        pass
    pass
    pass

    def destroy_serial(self):
        self.comPort.close()
        pass
    pass

    def get_port_info(self):
        if self.isopen:
            baud = self.comPort.baudrate
            name = self.comPort.name
        else:
            baud = 0
            name = "No connection!"
        return (baud, name)
    pass

    def is_open(self):
        return self.open
    pass
pass

class ConnectionData():
    """
    Documentation
    """
    def __init__(self, port, baud):
        self.baud = baud
        self.port = port
        pass
    pass